﻿namespace Pibby.Core.Opml
{
    using System;
    using System.IO;
    using System.Text;
    using System.Xml;

	/// <summary>
	/// OPML is the defacto podcast subscription import and export format.
	/// </summary>
	public sealed class OpmlDocument
	{
		#region Xml Constants

		const string OpmlNode = "opml";
		const string VersionAttr = "version";
		const string HeadNode = "head";
		const string TitleNode = "title";
		const string OwnerNameNode = "ownername";
		const string OwnerIdNode = "ownerId";
		const string DateModifiedNode = "datemodified";
		const string BodyNode = "body";
		const string OutlineNode = "outline";

		#endregion

		#region Constructors

		public OpmlDocument()
        {
			this.Outlines = new OutlineCollection();
        }

		public static OpmlDocument Load(TextReader reader)
		{
			if (reader == null)
				throw new ArgumentNullException("reader", Resources.Opml_NullObjectError);

			var document = new XmlDocument();
			document.Load(reader);

			return Load(document);
		}
		
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes", MessageId = "System.Xml.XmlNode")]
		public static OpmlDocument Load(XmlDocument xmlDoc)
		{
			if (xmlDoc == null)
				throw new ArgumentNullException("xmlDoc", Resources.Opml_NullObjectError);

			var opml = new OpmlDocument();

			foreach (XmlNode opmlNode in xmlDoc.ChildNodes)
			{
				if (opmlNode.Name.Equals(OpmlNode, StringComparison.CurrentCultureIgnoreCase))
				{
					if (opmlNode.Attributes[VersionAttr] != null)
						opml.Version = opmlNode.Attributes[VersionAttr].Value;

					foreach (XmlNode sectionNode in opmlNode.ChildNodes)
					{
						if (sectionNode.Name.Equals(HeadNode, StringComparison.CurrentCultureIgnoreCase))
						{
							foreach (XmlNode child in sectionNode.ChildNodes)
							{
								if (child.Name.Equals(TitleNode, StringComparison.CurrentCultureIgnoreCase))
								{
									opml.Title = child.InnerText;
								}
								else if (child.Name.Equals(OwnerNameNode, StringComparison.CurrentCultureIgnoreCase))
								{
									opml.OwnerName = child.InnerText;
								}
								else if (child.Name.Equals(OwnerIdNode, StringComparison.CurrentCultureIgnoreCase))
								{
									opml.OwnerId = child.InnerText;
								}
								else if (child.Name.Equals(DateModifiedNode, StringComparison.CurrentCultureIgnoreCase))
								{
									opml.ModifiedDate = DateTime.Parse(child.InnerText).ToUniversalTime ();
								}
							}
						}
						else if (sectionNode.Name.Equals(BodyNode, StringComparison.CurrentCultureIgnoreCase))
						{
							foreach (XmlNode outlineNode in sectionNode.ChildNodes)
							{
								Outline newOutline = Outline.Create((XmlElement)outlineNode);

								if (newOutline.XmlUrl == null)
								{
									foreach (XmlNode innerNode in outlineNode.ChildNodes)
									{
										Outline outline = Outline.Create((XmlElement)innerNode);

										if (outline.XmlUrl != null)
											opml.Outlines.Add(outline);
									}
								}
								else
								{
									opml.Outlines.Add(newOutline);
								}
							}
						}
					}
				}
			}

			return opml;
		}

		#endregion

		#region Properties

		public OutlineCollection Outlines
		{
			get;
			private set;
		}

		public string Version
		{
			get;
			set; 
		}

		public string Title
		{
			get;
			set;
		}

		public string OwnerName
		{
			get;
			set;
		}

		public string OwnerId
		{
			get;
			set;
		}

		public DateTime ModifiedDate
		{
			get;
			set;
		}

		public static string FileExtension
		{
			get { return ".opml"; }
		}

		public static string FileFilter
		{
			get { return "OPML Files (*.opml)|*.opml|XML Files (*.xml)|*.xml"; }
		}

		#endregion

		#region Methods

		public override string ToString()
		{
			var builder = new StringBuilder ();

			var settings = new XmlWriterSettings();
			settings.Indent = true;
			settings.IndentChars = ("    ");

			using (var writer = XmlWriter.Create(builder, settings))
			{
                writer.WriteStartElement(OpmlNode);
				writer.WriteAttributeString(VersionAttr, "1.0");
				writer.WriteStartElement(HeadNode);
				
				writer.WriteStartElement("title");
				writer.WriteString(this.Title);
				writer.WriteEndElement(); // title

				writer.WriteStartElement("dateCreated");
				writer.WriteString(this.ModifiedDate.ToString(System.Globalization.CultureInfo.CurrentUICulture));
				writer.WriteEndElement(); // dateCreated

				writer.WriteEndElement(); // head

                writer.WriteStartElement(BodyNode);

				foreach (var outline in this.Outlines)
				{
					writer.WriteStartElement(Outline.OutlineNode);

					writer.WriteAttributeString(Outline.TextAttribute, outline.Text);
					writer.WriteAttributeString(Outline.HtmlAttribute, outline.HtmlUrl.AbsoluteUri);
					writer.WriteAttributeString(Outline.TitleAttribute, outline.Title);
					writer.WriteAttributeString(Outline.TypeAttribute, outline.OutlineType.ToString());
					writer.WriteAttributeString(Outline.XmlAttribute, outline.XmlUrl.AbsoluteUri);

					writer.WriteEndElement();
				}

				writer.WriteEndElement(); // body

                writer.WriteEndElement(); // opml
            }
			return builder.ToString();
		}

		#endregion

	}
}
